<?php
namespace Handler;

use App\Library\Log;
use App\Library\TextLog;
use App\Logger;
use Core\DzInternetProtocol;
use Core\CommandProtocol;
use Core\Model;
use Core\TbRedis;
use Helper\Log as stdLog;
use L\Notify;

class Config
{
    //token
    protected $token;
    protected $taskProcess;
    protected $cpu_num;

    //协议解析对象
    protected $command_protocol;

    //协议生成对象
    protected $build_protocol;

    //数据库操作类
    protected $db;
    protected $workerdb=array();

    //探霸链接列表
    protected $client;
    protected $heartlist;
    protected $ouibase;


    //redis连接句柄
    protected $redis;
    protected $wkrRedis=array();

    //监听端口访问列表
    protected $listener;

    //系统配置列表--同步更新数据库--比如rf设备上报数据间隔
    protected $sysconfig;


    protected $atomic0;
    protected $atomic1;

    /**
     * 构造函数
     */
    public function __construct()
    {
        try {
            $this->cpu_num = CPU_NUM ;// swoole_cpu_num()*4;
            $this->protocol = new DzInternetProtocol();
            $this->command_protocol=new CommandProtocol();

            $this->atomic0 = new \Swoole\atomic();
            $this->atomic1 = new \Swoole\atomic();
            $this->atomic0->set(0);
            $this->atomic1->set(0);


            //创建客户端连接内存表
            $this->client = new  \Swoole\table(1024);
            $this->client->column('fd', \Swoole\table::TYPE_INT, 8);
            $this->client->column('id', \Swoole\table::TYPE_INT, 8);
            $this->client->column('token', \Swoole\table::TYPE_INT, 8);
            $this->client->column('newtoken', \Swoole\table::TYPE_INT, 8);
            $this->client->column('deviceID', \Swoole\table::TYPE_STRING, 64);
            $this->client->column('tbid', \Swoole\table::TYPE_INT, 8);
            $this->client->column('lasttime', \Swoole\table::TYPE_INT, 12);
            $this->client->column('lock', \Swoole\table::TYPE_INT, 4);

            $this->client->column('devicetype', \Swoole\table::TYPE_STRING, 64);//设备类型
            $this->client->column('encodetype', \Swoole\table::TYPE_STRING, 16); //加密算法
            $this->client->column('checktype', \Swoole\table::TYPE_STRING, 16);//校验算法
            $this->client->column('ability', \Swoole\table::TYPE_STRING, 16);//设备能力

            $this->client->column('firsttime', \Swoole\table::TYPE_STRING, 16);

            $this->client->column('status', \Swoole\table::TYPE_INT, 2);//当前状态
            $this->client->column('tcount', \Swoole\table::TYPE_INT, 8);//总包

            $this->client->column('hrtsd', \Swoole\table::TYPE_INT, 8);//发送心跳次数
            $this->client->column('hrtsderr', \Swoole\table::TYPE_INT, 8);
            $this->client->column('hrtstatus', \Swoole\table::TYPE_INT, 2);
            $this->client->column('openheart', \Swoole\table::TYPE_INT, 2);

            $this->client->column('hrtrsv', \Swoole\table::TYPE_INT, 8);//接收心跳次数
            $this->client->create();

            //创建客户端连接内存表
            $this->id2fd = new  \Swoole\table(1024);
            $this->id2fd->column('fd', \Swoole\table::TYPE_INT, 8);
            $this->id2fd->create();

            /*$this->flow = new \Swoole\Table(1024);
            $this->flow->column('fd', \Swoole\Table::TYPE_INT, 8);
            $this->flow->column('send', \Swoole\Table::TYPE_INT, 8); // 已发送数据流量
            $this->flow->column('receive', \Swoole\Table::TYPE_INT, 8); // 已接收数据流量
            //$this->flow->column('token_error', \Swoole\Table::TYPE_INT, 8); // token失败
            $this->flow->column('failed', \Swoole\Table::TYPE_INT, 8); // 解析失败数据流量
            $this->flow->create();*/


            //命令行列表
            $this->cmd = new  \Swoole\table(2048);
            $this->cmd->column('fd', \Swoole\table::TYPE_INT, 8);
            $this->cmd->column('wsfd', \Swoole\table::TYPE_INT, 8);
            $this->cmd->column('decode', \Swoole\table::TYPE_STRING, 256);
            $this->cmd->column('encode', \Swoole\table::TYPE_STRING, 256);
            $this->cmd->column('command', \Swoole\table::TYPE_STRING, 256);
            $this->cmd->column('data', \Swoole\table::TYPE_STRING, 256);
            $this->cmd->column('retry', \Swoole\table::TYPE_INT, 4);
            $this->cmd->column('timeout', \Swoole\table::TYPE_STRING, 20);
            $this->cmd->column('logid', \Swoole\table::TYPE_INT, 4);
            $this->cmd->create();

            $this->heartlist = new  \Swoole\table(32768);
            $this->heartlist->column('fd', \Swoole\table::TYPE_INT, 8);
            $this->heartlist->column('tbmac', \Swoole\table::TYPE_STRING, 64);
            $this->heartlist->column('senderro', \Swoole\table::TYPE_INT, 4);
            $this->heartlist->column('token', \Swoole\table::TYPE_STRING, 12);
            $this->heartlist->column('intime', \Swoole\table::TYPE_STRING, 11);
            $this->heartlist->column('timer', \Swoole\table::TYPE_INT, 4); //分钟
            $this->heartlist->column('sendtime', \Swoole\table::TYPE_STRING, 11);//最后一次发送时间
            $this->heartlist->column('resvtime', \Swoole\table::TYPE_STRING, 11);//最后一次发送时间
            $this->heartlist->column('status', \Swoole\table::TYPE_INT, 1);//发送回执状态
            $this->heartlist->column('hrtstatus', \Swoole\table::TYPE_INT, 2);
            $this->heartlist->create();

        } catch (Exception $e) {
            echo $e->getMesasge();
        }

    }


    /**
     */
    public function getFlow()
    {
        return $this->flow;
    }

    /**
     * 获取探霸KEY
     * @param $fd
     * @return mixed
     */
    public function getLoggerInfo($fd)
    {
        return [
            'tbMac' => $this->client->get($fd, 'deviceID'),
            'method' => 'cfgLog',
            'type' => 'config',
        ];
    }



    /**
     * 主进程Master进程启动回调方法    初始化配置参数
     * @param  SwooleServer $serv    swoole对象
     */
    public function onStart($serv)
    {
        echo "TBserver config Master start \n";
        $thismstredis=TbRedis::init("tanbaservercfginit");
        // 探霸状态初始化
        foreach ($thismstredis->keys('DZ*') as $ky) {
            $thismstredis->hMset($ky,array('status'=>0,  'fd' =>0,'updatetime' => time()));
        }
    }
    // 2. Master进程拉起Manager进程
    public function onManagerStart( $serv )
    {
        cli_set_process_title("tanba_TbServer_manager");
        echo "\n TbServer Manager Start ...master_pid=>".$serv->master_pid.",manager_pid=>".$serv->manager_pid."\n";
        //   $serv->locProcess::setaffinity(array $cpu_set);//表示绑定哪些CPU核，如array(0,2,3)表示绑定CPU0/CPU2/CPU3

        //读取redis命令进程
        ( new \Swoole\Process(function ($process) use($serv) {
            cli_set_process_title("tanba_redis_read");
            while (true) {
                //连接redis
                while (true) {
                    try {
                        $redis =TbRedis::init("tbcfgcommdersv");
                        break;
                    } catch (\RedisException $e) {
                        stdLog::print('redis connect fail', $e->getCode(), $e->getLine());
                        sleep(2);
                    }
                }

                while (true) {
                    try {
                        // 从队列中获取命令
                        /**
                         * @var \Redis $redis
                         */
                        $data = json_decode($redis->brPop(['command'], 0)[1], true);

                        /**
                         * array:6 [
                            "type" => "command"
                            "command" => "setupdate"
                            "deviceid" => "DZ180627010000000G"
                            "data" => array:5 [
                            "frimware" => "2"
                            "softver" => "40"
                            "hardver" => "65"
                            "id" => "2"
                            "name" => "33333333333333333333"
                            ]
                            "wsfd" => 6
                            "time" => "2018-07-26 10:10:26"
                            ]
                         */
                        if(is_array($data)&& !empty($data)){
                            $fd = $this->id2fd->get($data['deviceid'], 'fd');
                            if (false == $fd ) {
                                $arr = array(
                                    'type'     => 'send',
                                    'msg'      => '该设备未连接上配置服务器',
                                    'deviceID' => $data['deviceid'],
                                    'wsfd'     => $data['wsfd'],
                                    'command'  => $data['command'],
                                    'token'    => 0,
                                    'before'   => 0,
                                    'time'     => $this->microtime_float(),
                                );
                                $redis->lPush('result', json_encode($arr));
                            }/* elseif ($this->client->get($fd, 'lock')) {
                                //判断锁的状态
                                $arr = array(
                                    'type'     => 'send',
                                    'msg'      => '设备锁定中不可操作，稍后再试',
                                    'deviceID' => $data['deviceid'],
                                    'wsfd'     => $data['wsfd'],
                                    'token'    => $this->client->get($fd, 'token'),
                                    'command'=>$data['command'],
                                    'before'   => '',
                                    'time'     => $this->microtime_float(),
                                );
                                $redis->lPush('result', json_encode($arr));
                            } */else {
                                $cmdData = array('type' => $data['command'], 'token' => $this->client->get($fd, 'token'), 'data' => $data['data']);
                                //dump($cmdData);
                                $binCmd = $this->command_protocol->setcommand($data['command'], $cmdData['data'],$cmdData['token']);
                                echo "\n =====conmmand :".json_encode($data)."==== \n";
                                //dump($binCmd);
                                if(!$binCmd || isset($binCmd['code']) || is_array($binCmd)){
//                            if(!$binCmd){
                                    $arr = array(
                                        'type'     => 'send',
                                        'msg'      => '命令或参数错误'.json_encode($binCmd['data'],JSON_UNESCAPED_UNICODE),
                                        'deviceID' => $data['deviceid'],
                                        'wsfd'     => $data['wsfd'],
                                        'token'    => $this->client->get($fd, 'token'),
                                        'command'  => $data['command'],
                                        'before'   => '',
                                        'time'     => $this->microtime_float(),
                                    );
                                    $redis->lPush('result', json_encode($arr));
                                }else{
                                    $sdcommand=array(
                                        'fd'      => $fd,
                                        'wsfd'    => $data['wsfd'],
                                        'retry'   => 0,
                                        'command' => $data['command'],
                                        'decode'  => json_encode($data),
                                        'encode'  => $binCmd,
                                        'data'    => isset($data['data']) ? json_encode($data['data']) : '',
                                        'logid'   => 0,
                                        'timeout' =>0 ,
                                    );
                                    $this->cmd->set($fd, $sdcommand);
                                    //推送命令数据到进程发送给探霸
                                    $taskId = mt_rand(0, ($this->cpu_num - 1));
                                    $this->taskProcess[$taskId]->push(json_encode($sdcommand));
                                }


                            }

                        }

                    } catch (\RedisException $e) {
                        stdLog::print('redis error [' . $e->getMessage() . ']', $e->getCode(), $e->getLine());
                        break;
                    }

                }
            }
        }, false))->start();

        //超时重发命令定时器
        $handler=$this;
        $serv->cmdCallback = new \Swoole\Process(function ($process) use($serv, $handler) {
            while (true) {
                while (true) {
                    try {
                        $redis =TbRedis::init("confcallback");
                        break;
                    } catch (\RedisException $e) {
                        stdLog::print('redis connect fail', $e->getCode(), $e->getLine());
                    }
                }

                try {
                    while (true) {
                        $time = $this->microtime_float();
                        foreach ($handler->cmd AS $k => $r) {
                            $dtime=$time - (int)$r['timeout'];
                            if($dtime>1000 && $r['retry']>0){
                                if ($r['retry'] <= 3) {
                                    $taskId = mt_rand(0, ($this->cpu_num - 1));
                                    $this->taskProcess[$taskId]->push(json_encode($r));
                                } else {
                                    try {
                                        $arr = array(
                                            'type'     => 'result',
                                            'msg'      => '响应超时',
                                            'deviceID' => $handler->client->get($r['fd'], 'deviceID'),
                                            'wsfd'     => $r['wsfd'],
                                            'command'  => $r['command'],
                                            'token'    => 0,
                                            'before'   => 0,
                                            'after'    => 0,
                                            'time'     => $dtime,
                                        );
                                        $redis->lPush('result', json_encode($arr));
                                        $handler->cmd->del($r['fd']);
                                        $handler->client->set($r['fd'], array('lock' => 0));
                                    } catch (\Exception $e) {
                                        break;
                                    }
                                }
                            }

                        }
                        sleep(20);
                    }
                } catch (\RedisException $e) {
                    $redis =TbRedis::init("confcallback");
                }
            }
        });
        $serv->cmdCallback->start();

     /*   //心跳发送进程

        (new \Swoole\Process(function ($process) use($serv) {
            cli_set_process_title('configserver_heart_senter_Process');
            while (1) {
                sleep(3);
                foreach ($this->heartlist as $row) {
                    $fd=$row['fd'];
                    if ($serv->exist($fd)) {
                        if(isset($row['token']) && isset($row['tbmac']) && isset($row['sendtime']) && isset($row['timer'])){
                            $hrtstatus=$this->heartlist->get($fd, 'hrtstatus');
                            if(((time()-$row['sendtime'])>=$row['timer']*60 )|| $hrtstatus==1){

                                if($hrtstatus==1){
                                    $errcounts=$this->heartlist->incr($fd,'senderro', 1);//心跳发送数量
                                    if($errcounts>3){
                                        $this->client->set($fd, array('status' => 7));
                                        $serv->close($fd);
                                        continue;
                                    }

                                }

                                $token=$this->heartlist->get($fd, 'token');
                                $ret =  $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'1000'],['do'=>'heart'],$token));

                                $this->heartlist->set($fd,['hrtstatus'=>1,'sendtime'=>time()]);
                                if($ret){
                                    $this->client->set($fd,['hrtstatus'=>1]);//发送标记hrtstatus，收到响应后取消
                                    $this->client->incr($fd,'hrtsd', 1);//心跳发送数量

                                    echo "\n +++Heart! [{$row['tbmac']}] ".date('H:i:s',$row['sendtime'])." ++++ \n";
                                }
                            }

                        }

                    }

                }
            }


        }, false, false))->start();
*/

        (new \Swoole\Process(function ($process) use($serv) {
            cli_set_process_title('configserver_token_sent_Process');
            while (1) {
                sleep(TOKEN_INTERVAL);
                foreach ($this->client as $row) {
                    $fd=$row['fd'];
                    if ($serv->exist($fd) && isset($row['token'])) {
                        $newtoken=time();
                        $token=$row['token'];
                        $this->sendtoclient($serv,$fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'1000'],['do'=>'sdtoken','token'=>$newtoken],$token));
                        echo "sent [{$row['deviceID']}] token!-{$newtoken}";

                        $this->client->set($fd, array('token' =>$newtoken,'newtoken' =>$newtoken,'hrtsderr'=> 0,'openheart'=>0));
                        $this->heartlist->set($fd,['token'=>$newtoken,'status'=>0,'senderro'=>0]);
                    }

                }
            }

        }, false, false))->start();


        // 接收过期的心跳键值, 关闭客户端连接
        $heartListener = (new \Swoole\Process(function ($process) use ($serv){
            while ($data = $process->read()) {
                if(strpos($data,'heart:')){
                    list($prefix, $fd) = explode(':', $data);

                    if ($prefix !== 'heart') continue;

                    // 异常
                    /**
                     * 心跳不存在
                     */
                    $deviceId = $this->client->get($fd, 'deviceID');
                    Log::cfgLog($deviceId)->alert(Log::HEART_LISTEN, [
                        'fd'     => $fd,
                        '心跳异常' => "服务器长时间没有收到心跳"
                    ]);

                    Logger::cfgLog($deviceId)->debug("heart error|{$fd}");

                    // 如果数据中可以读取出客户端连接编号，且该客户端处于连接状态， 关闭
                    if ($fd && $serv->exist($fd)) {
                        $msg = "++ close client {$fd}| too long no heart recv! ----------- " . date('Y-m-d H:i:s');
                        echo $msg;


                        $deviceId = $this->client->get($fd, 'deviceID');
                        /**
                         * 心跳不存在
                         */
                        Log::cfgLog($deviceId)->alert(Log::HEART_LISTEN, [
                            'fd'     => $fd,
                            '心跳异常' => "服务器长时间没有收到心跳"
                        ]);

                        Logger::cfgLog($deviceId)->debug($msg);

                        $this->client->set($fd, array('status' => Logger::CLOSED_VIA_HEART_DIE));
                        echoMsg($msg, null, true, null, 'heart');
                        $serv->close($fd);
                    }
                }

            }
        }));

        $heartListener->start();

        // 监听心跳超时, 如果超时则交给 heartListener 子进程去处理
        (new \Swoole\Process(function () use($serv, $heartListener) {
            cli_set_process_title('configserver_heart_subscribe_Process');

            $heartRedis = TbRedis::init("heart_process");
            $heartRedis->psubscribe(['__keyevent@'.config('redis')['db'].'__:expired'], function($redis, $chan, $chanName,$data) use ($heartListener){
                $heartListener->write($data);
            });

        }, false, false))->start();


    }

    /**
     * worker进程启动回调方法
     * @param  SwooleServer $serv    swoole对象
     * @param  int $worker_id         worker进程id号
     */
    public function onWorkerStart($serv, $worker_id)
    {
        //连接数据库
        while (true) {
            try {
                $serv->db[$serv->worker_id] = Model::init("tbcfgswrwkr".$serv->worker_id);
                break;
            } catch (\PDOException $e) {
                stdLog::print('mysql connect fail on worker['.$serv->worker_id.']', $e->getCode(), $e->getLine());
            }
        }

        //连接redis
        while (true) {
            try {
                $serv->redis[$serv->worker_id] =TbRedis::init("configrsv.".$serv->worker_id);
                break;
            } catch (\PDOException $e) {
                stdLog::print('redis connect fail on worker['.$serv->worker_id.']', $e->getCode(), $e->getLine());
            }
        }

    }

    /**
     * 连接回调方法
     * @param  \Swoole\Server $serv    swoole对象
     * @param  int $fd                连接句柄编号
     */
    public function onConnect($serv, $fd)
    {
        $info = $serv->connection_info($fd);
        $theredis= $serv->redis[$serv->worker_id];

        $token = time();
        // 客户端连接， 保存客户端数据，和心跳
        $this->client->set($fd, array('token' =>$token, 'newtoken' =>$token,'fd' => $fd, 'lasttime' => time(), 'lock' => 0,'firsttime'=>microtime_float()));
        $this->heartlist->set($fd,['fd'=>$fd,'intime'=>$token,'sendtime'=>$token,'timer'=>HEART_INTERVAL,'hrtstatus'=>0,'senderro'=>0]);

        // @test 流量设置初始化
        //$this->flow->set($fd, ['fd' => $fd, 'send' => 0, 'receive' => 0, 'failed' => 0]);

        $msg= "\n ==== onConnect :worker_id=".$serv->worker_id.",fd=({$fd}),from=".$info['remote_ip'].":".$info['remote_port'] ." : ". date('Y-m-d H:i:s') ." ====\n";
        echo $msg;
        echoMsg($msg,$theredis,true,'confredis','config');
    }

    /**
     * 接收数据回调方法
     * @param  SwooleServer $serv    swoole对象
     * @param  int $fd                连接句柄编号
     * @param  int $form_id           reactor线程号
     * @param  bin $data              接收到的二进制数据
     */
    public function onReceive($serv, $fd, $form_id, $data)
    {
        $theRedis=$serv->redis[$serv->worker_id];
        $theDb=$serv->db[$serv->worker_id];


        $hexStr=bin2hex($data);

        $dataBytes = strlen($hexStr) / 2; // 收到数据的字节数

        $this->client->incr($fd,'tcount', 1);

        //$this->flow->incr($fd, 'receive', $dataBytes);
        // $this->client->incr($fd, 'bytes', $recvBytes);

        //dump('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');

        //
        // @test 收到流量计算, 连接所耗费的流量忽略
        if ($deviceID = $this->client->get($fd, 'deviceID')) {
            $tbKey = Logger::tb($deviceID);
            $receiveBytes = $theRedis->hGet($tbKey, 'configReceiveBytes') ?: 0;

            $receiveBytes += $dataBytes;

            // 简化单词数量
            TextLog::cfgLog($deviceID)->debug('recv:', [
                'cur' => $dataBytes . 'B',
                'all' => number_format($receiveBytes / 1024, 3) . 'KB',
            ]);

            $theRedis->hSet($tbKey, 'configReceiveBytes', $receiveBytes);
        }
        //dump('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<');
//        echo  PHP_EOL."{$fd} receive:".$hexStr.PHP_EOL;

        $token=$this->client->get($fd, 'token');
        $data = $this->protocol->parsein($hexStr,$token);    //解析数据

        if(!$data || !isset($data['data']) || !isset($data['header'])) {
            //@test 解析错误流量
            $this->client->incr($fd, 'failed', $dataBytes);
            return false;
        }

        if($data['header']['dt']=='00'){
            switch ($data['header']['sdt']) {
                case '0011': // 连接请求帧
                    if(isset($data['data']['encodeType'])){
                        $this->client->set($fd, array('encodetype' =>$data['data']['encodeType']['code'],'checktype'=>$data['data']['encodeType']['check']));
                    }
                    if(isset($data['data']['device'])){
                        $tanbaMac=$data['data']['device']['mac'];

                        //如果连接已经存在则关闭上一个连接
                        if ($this->id2fd->exist($tanbaMac)) {
                            $oldLink = $this->id2fd->get($tanbaMac, 'fd');
                            if (isset($oldLink) && !empty($oldLink) && $oldLink != $fd) {
                                $this->client->del($oldLink);
                                $this->client->set($oldLink, array('status' => Logger::CLOSED_VIA_LINK_TWICE));
                                Log::cfgLog($tanbaMac)->debug('旧连接关闭', [
                                    'fd' => $fd,
                                ]);
                                $serv->close($oldLink);
                            }
                        }

                        //$serv->close($fd);

                        $theRedis->hMset($tanbaMac, array(
                            'encodetype'=>$data['data']['encodeType']['code'],
                            'checktype'=>$data['data']['encodeType']['check'],
                            'basedeviceinfo' => json_encode($data['data']['device'])
                        ));

                        $time0=$this->client->get($fd,'firsttime');
                        $tbmsg= "\n ==== onConnect ({$fd})[{$tanbaMac}]: receive connect info times :".((microtime_float()-$time0)/1000)." s ===== ".PHP_EOL;
                        echo $tbmsg;
                        echoMsg($tbmsg,$theRedis,true,'confredis','config');

                        /**
                         * 探霸控制器上线时间和次数
                         */
                        $tbMacKey = Logger::tb($tanbaMac);
                        $connectCounts = $theRedis->hGet($tbMacKey, 'cfg_connect_counts') ?: 0;
                        $theRedis->hMSet($tbMacKey, [
                            'cfg_connect_counts' => ++$connectCounts,
                            'cfg_connect_at'    => time()
                        ]);

                        Log::cfgLog($tanbaMac)->info(Log::MANAGER_FRAME, [
                            'fd' => $fd,
                            'onConnect' => '连接花费时间为: ' . ((microtime_float()-$time0)/1000) . 's',
                            '连接次数'    => $connectCounts
                        ]);

                        //记录版本、类型和mac标识
                        $this->client->set($fd, array('deviceID' =>$tanbaMac));
                        $this->id2fd->set($tanbaMac, array('fd' => $fd));

                        //回应连接段连接请求响应
                        $serv->send($fd, $this->protocol->setCommand(['dt'=>'00','sdt'=>'0100']));
                        sleep(1);

                        //发送token
                        $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'1000'],['do'=>'sdtoken','one'=>0],$token));

                        $this->heartlist->set($fd,['fd'=>$fd,'tbmac'=>$tanbaMac,'sendtime'=>time(),'token'=>$token,'timer'=>HEART_INTERVAL]);

                        sleep(1);
                        // 状态获取帧
                        //$serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'0110'],['do'=>'getdeviceinfo'],$token));
                        //$serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'0110'],['do'=>'getdevicestatus'],$token));
                        $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'0110'],['do'=>'getWifiChannel'],$token));
//                        sleep(1);
//                        $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'1000'],['do'=>'getheart'],$token));

                        sleep(1);
                        // 能力获取帧
                        $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'0000'],['do'=>'getability'],$token));


                        $deviceID=$tanbaMac;
                        $tanbaid = $theRedis->hGet($deviceID, 'tbid');
                        //dump('-------');
                        //dump($deviceID, $tanbaid);

                        // 如果探霸id不存在
                        if(!isset($tanbaid) || empty($tanbaid)) {
                            //dump('探霸IDbu存在>>>>>>>>>>');
                            $deviceID=$data['data']['device']['mac'];
                            //dump('deviceID:' . $deviceID);
                            //     //重新设置 插入或者更新数据-------------------
                            $insert_data = array(
                                'mac' => $deviceID,
                                'token' => $this->client->get($fd, 'token'),
                                'cfgfd' => $fd,
                                'cfgtoken' => $this->client->get($fd, 'token'),
                                'cfgserverip' => IP,
                                'cfgserverport' => PORT,
                                'cfglasttime' => time(),
                                'createtime' => time(),
                            );
                            //dump($insert_data);
                            $where = array('mac' => $deviceID);
                                                       if (!$theDb->has('device', $where)) {
                                //dump('不存在数据');
                                $tanbaid = $theDb->insert('device', $insert_data);
                                $tbmsg = "\n new TBdevice [" . $deviceID . "] insert successful \n";
                                //dump($tbmsg, $tanbaid);
                                echoMsg($tbmsg,$theRedis,true,'confredis','config');
                            }else{
                                //dump('存在，更新一下数据');
                                $theDb->update('device', $insert_data, $where);
                            }

                            $tanbaid = $theDb->get('device', 'id', $where);
                        }

                        //dump('-------');

                        $this->client->set($fd, array('tbid' =>$tanbaid));
                        if (!$theDb->has('device', ["id" => $tanbaid])) {
                            $theDb->insert("device_basehome", ["dvid" => $tanbaid, "nickname" => $deviceID]);
                        }

                        // 状态获取帧
                        $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'0110'],['do'=>'getdeviceloc'],$token));


                        $tbloction = $theDb->get("device_basehome", ["longitude", "latitude", 'hightland'], array("dvid" => $tanbaid));
                        $array_hmset_date = array(
                            'fd' => $fd,
                            'id' => $tanbaid,
                            'mac' => $deviceID,
                            'tbid' => $tanbaid,
                            'encodetype'=>$data['data']['encodeType']['code'],
                            'checktype'=>$data['data']['encodeType']['check'],
                            'basedeviceinfo' => json_encode($data['data']['device']),
                            'location' => json_encode($tbloction?$tbloction:[])
                        );
                        $theRedis->hMset($deviceID, $array_hmset_date);


                        $sttdata= array(
                            'type' => 'TBstat',
                            'fd' =>$fd,
                            'deviceid' => $deviceID,
                            'status' => 1,
                            'statustext' => 'online',
                            'time' => date('Y-m-d H:i:s',time()),
                        );
                        $this->sentMsgOut($serv,$sttdata,$theRedis);
                    }

                    break;
                case '0111':
                    //客户端请求断开--探霸可暂时不用
                    echo "=== close request 0111 ===\n";
//                    var_dump($data['data']);
//                    $serv->send($fd, $this->protocol->setCommand(['dt'=>'00','sdt'=>'0111','td'=>'10'],['do'=>'setcloseresponse','data'=>1],$token));//同意断开

                    break;
                case '1000':
                    //服务器要求断开客户端，然后客户端的回应，要求断开链接
                    echo "=== close request 1000 ===\n";
//

                    $this->sendWbsckResponse($serv,$fd,$data,$theDb);
                    break;

                default:
                    ;
            }
        }elseif ($data['header']['dt']=='01'){
           /* if($data['header']['sdt']=='0100'){
                echo "=== client erro report ===\n";
                if($data['data']['data']<>'00'){
                    //                //设置故障处理
//                $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'0101'],['do'=>'responseerro','data'=>2],$token));
//                sleep(1);
                    //断开请求
                    //1正常2重连3重发秘钥4强制断开
//                        $serv->send($fd, $this->protocol->setCommand(['dt'=>'00','sdt'=>'0111','td'=>'10'],['do'=>'setclose','data'=>2],$token));//客户度请求断开.同意断开
//                        $serv->send($fd, $this->protocol->setCommand(['dt'=>'00','sdt'=>'0111','td'=>'10'],['do'=>'setdowntime','cfg'=>1,'data'=>2],$token));
//
                }else{

                    var_dump($data['data']);
                }



            }
            else{
            }*/
            if(isset($data['data']['fromcmd']) ){
                $deviceID=$this->client->get($fd, 'deviceID');
                $tanbaid=$this->client->get($fd, 'tbid');
                echo PHP_EOL."{$fd} [{$deviceID}] return:".$data['data']['fromcmd'].PHP_EOL;
                switch ($data['data']['fromcmd']) {
                    case 'heart':
                        echo "\n {$deviceID} @";
                        $this->heartlist->set($fd,['resvtime'=>time(),'status'=>0,'hrtstatus'=>0,'senderro'=>0]);
                        $this->heartlist->set($fd,['senderro'=>0]);

                        $sttdata= array(
                            'type' => 'TBstat',
                            'fd' =>$fd,
                            'deviceid' => $deviceID,
                            'status' => 1,
                            'statustext' => 'online',
                            'time' => date('Y-m-d H:i:s',time()),
                        );
                        $this->sentMsgOut($serv,$sttdata,$theRedis);

                        $theRedis->set('heart:' . $fd, 1);
//                        $timer = $theRedis->hGet($deviceID, 'datatimer');
                        $theRedis->expire('heart:' . $fd,  2 * 60);

                        echo "\n ------------++ recv client {$fd}【{$deviceID}】's heart! ----------- " . date('Y-m-d H:i:s') . "|timer  2 min--------\n";
                        Logger::cfgLog($deviceID)->debug("heart| ------------++ recv client {$fd}【{$deviceID}】's heart! ----------- " . date('Y-m-d H:i:s') . "---timer  2 min--------");
                        $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'1000'],['do'=>'respheart'],$token));

                        break;
                    case 'getdeviceinfo':
//                        var_dump($data['data']['device']);
                        //插入或者更新数据库
//                            $rdata['device']['wktype'];//工作模式
//                            $rdata['device']['vltype'];//协议版本
//                            $rdata['device']['hdtype'];//硬件版本
//                            $rdata['device']['sttype'];//软件版本
                        /*"string" => "445a3138303632353031303030303030303140551201"
    "wktype" => "采集模式"
    "vltype" => "12"
    "subvltype" => 1
    "mvltype" => 2
    "hdtype" => "55"
    "subhdtype" => 5
    "mhdtype" => 5
    "sttype" => "40"
    "substtype" => 4
    "msttype" => 0
    "mac" => "DZ1806250100000001*/

                        $theRedis->hMset($deviceID, array(
                            'deviceinfo' => json_encode($data['data']['device'])
                        ));
                        break;
                    case 'getdevicestatus':
                        /**
                         * "device" => array:6 [
                            "webtype" => "4G"
                            "temperature" => 35
                            "temperature2" => -126
                            "testbattery" => 41473
                            "battery" => "20%"
                            "batterystatus" => "充电中"
                        ]

                         */

                        // @test 连接时探霸基础信息
                        Log::cfgLog($deviceID)->debug('连接时获取设备信息', [
                            '联网方式' => $data['data']['device']['webtype'],
                            '温度1' => $data['data']['device']['temperature'],
                            '温度2' => $data['data']['device']['temperature2'],
                            '剩余电量' => $data['data']['device']['battery'],
                            '电池状态' => $data['data']['device']['batterystatus'],
                        ]);
                        //dump($data['data']);
                        $clsmsg= "{$fd},[$deviceID],".implode(",",array_values($data['data']['device'])).",".date('Y-m-d H:i:s',time()).PHP_EOL;//erroreport
                        echoMsg($clsmsg,$theRedis,true,'confredis','config'.$deviceID);

                        $errret = array(
                            'type' => 'gettbstatus',
                            'command'=>'gettbstatus',
                            'wsfd' => 0,
                            'deviceID' => $deviceID,
                            'message' => '当前状态',
                            'msg' => '结果'.json_encode($data['data'],JSON_UNESCAPED_UNICODE),
                            'token' => 0,
                            'after' =>'',
                            'time' => 0,
                            'dvdata'=>$data['data']['device'],
                            'dvlantype'=>$data['data']['device']['webtype'],
                            'temperature'=>$data['data']['device']['temperature'],
                            'temperature2'=>$data['data']['device']['temperature2'],
                            'battery'=>$data['data']['device']['battery'],
                            'rawdata'=>$data['data']['rawstr'],
                            'returntime'=>date('Y-m-d H:i:s',time())
                        );

                        $theRedis->lPush('result', json_encode($errret));
                        break;
                    case 'getdeviceloc':
                        echo 'getdeviceloc----------------' , PHP_EOL;
                        //dump($data);
                        //dump($data);
                    ///dump('----------解析后');
                        /**
                         *
                        "data" => array:3 [
                            "location" => array:5 [
                                "typestr" => "00000010"
                                "type" => array:1 [
                                0 => "Galieo"
                                ]
                                "longitude" => "87.5756063\x00"
                                "latitude" => "43.8530788"
                                "hightland" => "777.6000"
                        ]

                         */
                        if(isset($data['data']['location']['longitude']) && isset($data['data']['location']['latitude'])){
                            $tbloction=array("longitude"=>$data['data']['location']['longitude'], "latitude"=>$data['data']['location']['latitude'], "hightland"=>$data['data']['location']['hightland']);
                            $theRedis->hMset($deviceID, array('location' => json_encode(array_merge($tbloction,['time'=>time()]))));
                            echo "\n {$deviceID}-------location {$data['data']['location']['longitude']},{$data['data']['location']['latitude']} -------- \n";
                            //dump($deviceID, $tbloction);
                            $retloc=getLocationBaselist(['devmac'=>$deviceID],$theDb,$theRedis);

                            $wsfd=$this->cmd->get($fd, 'wsfd');
                            //dump($retloc);
                            $theRedis->set('httpCommandresult_'.$wsfd, json_encode($retloc));
                        }

                        break;
                    case 'getlbslocbase':
                        echo 'I here';
                        if(isset($data['data'])){
                            $datestr=json_encode($data['data']['data']);
//                            if(is_numeric($data['data']['data'])){
//                                $theRedis->hMset($deviceID, array('locationbase' => $data['data']['data']));
//                            }else{
//                                $data['data']['data'] =$data['data']==2?"失败":"命令不支持";
//                            }
                            $theRedis->hMset($deviceID, array('locationbase' => $datestr));
                            $wsfd=$this->cmd->get($fd, 'wsfd');
                            $theRedis->set('httpCcmdresult_locbase_'.$wsfd,  $datestr);

                        }

                        break;
                    case 'getsmbaseinfo':
                        //var_dump($data['data']);
                        break;
                    case 'getability':
//                        var_dump($data['data']);
                        //dump($data['data']);
                        $theRedis->hMset($deviceID, array('ablity'=>json_encode($data['data']['base'])));
                        break;

                    case 'setupdate':
                        $clsmsg="\n  [$deviceID] setupdate return:".json_encode($data['data'],JSON_UNESCAPED_UNICODE). date('Y-m-d H:i:s',time());
                        echoMsg($clsmsg,$theRedis,true,'confredis','config');

                        //dump('控制服务器:' . $data['data']);
                        if(isset($data['data']['code'])){
                            $theRedis->hMset($deviceID, array('updatastat'=>$data['data']['code']==1? 10:-1));
                        }
                        break;
                    case 'notfind':
                        var_dump($data['data']);
                        $clsmsg="\n notfind return [$deviceID]:".json_encode($data['data'],JSON_UNESCAPED_UNICODE). date('Y-m-d H:i:s',time());
                        echoMsg($clsmsg,$theRedis,true,'confredis','config');

                        Log::cfgLog($deviceID)->emergency(Log::CONTROL_FRAME, [
                            'fd' => $fd,
                            'NotFound' => '设备未找到!',
                            '原始数据' => $data['data'],
                        ]);

                        break;
                    case 'geterro':
                        echo "Erro report ({$fd}) [$deviceID]:".$data['data']['rawstr']." ".date('Y-m-d H:i:s',time());//erroreport

                        $errret = array(
                            'type' => 'geterro',
                            'command'=>'geterro',
                            'wsfd' => 0,
                            'deviceID' => $deviceID,
                            'message' => '当前故障状态',
                            'msg' => '结果'.json_encode($data['data'],JSON_UNESCAPED_UNICODE),
                            'token' => 0,
                            'after' =>'',
                            'time' => 0,
                            'rawdata'=>$data['data']['rawstr'],
                            'returntime'=>date('Y-m-d H:i:s',time())
                        );

                        // 设备异常
                        Log::cfgLog($deviceID)->emergency(Log::CONTROL_FRAME, [
                            'fd' => $fd,
                            '当前故障状态' => $data['data'],JSON_UNESCAPED_UNICODE,
                            '原始数据' => $data['data']['rawstr'],
                        ]);

                        $theRedis->lPush('result', json_encode($errret));
//
//                        if(in_array(substr($data['data']['rawstr'],0,4),['0041','0042']) ){
//                            $clsmsg="\n Erro report ({$fd}) [$deviceID]:".json_encode($data['data'],JSON_UNESCAPED_UNICODE). date('Y-m-d H:i:s',time());
//                            echo $clsmsg;
//                            echoMsg($clsmsg,$theRedis,true,'confredis','config');
//                        }else{
//                            var_dump($data['data']['rawstr']);
//                        }


                        break;
                    case 'setheart':
                        if($data['data']=='成功'){
                            $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'1000'],['do'=>'getheart'],$token));
                        }
                        break;
                    case 'getheart':
                        if($data['data']['data']>0){
                            $theDb->update('device', ['cfgheart'=>$data['data']['data']], array('mac' => $deviceID));
                            $this->heartlist->set($fd,['timer'=>$data['data']['data']]);
                            $theRedis->hMset($deviceID, ['datatimer'=>$data['data']['data']]);
                        }

                        break;
                    case 'getWifiChannel':
                        //dump($data);
                        break;
                    case 'setWifiChannel':
                        break;
                    case 'setWifiSSID':
                        break;
                    case 'getWifiSSID':
                        dump($data);
                        break;
                    case 'multi':
                        //dump($data);
                        break;
                    default:
                        var_dump($data['data']);
                        break;
                }


                if(!in_array($data['data']['fromcmd'],['heart','sdtoken']) ){
                    $this->sendWbsckResponse($serv,$fd,$data,$theDb);
                }

            }else{
                $deviceID=$this->client->get($fd, 'deviceID');
                $htmsg= "\n Erro:=== ({$fd})[{$deviceID}]receive erro ,decode type  not find :{$hexStr} ===\n";
                echo $htmsg;
                echoMsg($htmsg,$theRedis,true,'confredis','config');
                Log::cfgLog($deviceID)->alert(Log::CONTROL_FRAME, [
                    'fd'       => $fd,
                    '帧类型错误' => "没有对应的控制帧类型",
                    '原始数据'  => $hexStr
                ]);
            }
        }
        elseif ($data['header']['dt']=='10'){
            ;
           //数据传输-数据服务中进行
//            if( !isset($data['data'])||!is_array($data['data']) ) return;
        }
        $data=null;
        unset($data);

    }

    /**
     * 命令执行结果反馈，发给来自websocket的命令请求
     * @param $serv
     * @param $fd
     * @param $data
     * @Created by: xiebaoxin@163.com
     * @DateTime: 2018/5/8 9:49
     */
    public function sendWbsckResponse($serv,$fd,$data,$theDb)
    {
        if(!in_array($data['data']['fromcmd'],['heart','sdtoken']) ){
            $time0= $this->cmd->get($fd,  'timeout');
            $deviceID=$this->client->get($fd, 'deviceID');
            $wsfd=$this->cmd->get($fd, 'wsfd');
            dump($deviceID, $data['data'], $wsfd);
            if($deviceID && $wsfd){
                $ret = array(
                    'type' => $data['data']['fromcmd'],
                    'command'=>$data['data']['fromcmd'],
                    'wsfd' => $wsfd,
                    'deviceID' => $deviceID,
                    'message' => '返回成功',
                    'msg' => '返回结果'.json_encode($data['data'],JSON_UNESCAPED_UNICODE),
                    'token' => $this->client->get($fd, 'token'),
                    'after' => $data['data']['rawstr'],
                    'time' => $this->microtime_float()-(int)$time0,
                    'rawdata'=>json_encode($data['data']),
                    'returntime'=>date('Y-m-d H:i:s',time())

                );

                if($data['data']['fromcmd']=='getdevicestatus'){
                  $pbret=array(  'dvdata'=>$data['data']['device'],
                            'dvlantype'=>$data['data']['device']['webtype'],
                            'temperature'=>$data['data']['device']['temperature'],
                            'temperature2'=>$data['data']['device']['temperature2'],
                            'battery'=>$data['data']['device']['battery']);
                    $ret=array_merge($ret,$pbret);
                }

                try {
                    $serv->redis[$serv->worker_id]->lPush('result', json_encode($ret));//推送到前端

                    //更新日志状态
                    if ($this->cmd->get($fd)) {
                        $cmdRet = array(
                            'Status' => 1,
                            'ResultBefore' => $data['data']['rawstr'],
                            'ResultAfter' => json_encode($data['data'],JSON_UNESCAPED_UNICODE),
                        );
                        $theDb->update('device_command_log', $cmdRet, array('id' => $this->cmd->get($fd, 'logid')));
                    }

                } catch (\RedisException $e) {
                    stdLog::print("unknown error[{$e->getMessage()}]", $e->getCode(), $e->getLine());
                }

                $this->cmd->del($fd);
                $this->client->set($fd, array('lock' => 0));
            }

        }

    }

    /**
     * 状态变化通知
     * @param $serv
     * @param $sttdata
     * @param $theRedis
     * @Created by: xiebaoxin@163.com
     * @DateTime: 2018/5/8 9:50
     */
    public function sentMsgOut($serv,$sttdata,$theRedis)
    {
        // 给订阅频道发送信息, 在WebSocket.php中
        $theRedis->publish(getDbNum() . REDIS_TB_STATUS_CONFIG, json_encode($sttdata));

        $theRedis->hMset($sttdata['deviceid'], array(
            'cfgfd' => $sttdata['fd'],
            'cfgupdatetime' => $sttdata['time'],
            'cfgstatus' => $sttdata['status']
        ));

    }


    public function onTask($serv, $task_id, $from_id, $data )
    {
     ;
    }


    public function onFinish($serv, $task_id, $data)
    {
        // 异步Task时有效
        echo $data; //"\n # {$task_id} Task finished, result:" .$data.PHP_EOL; //       echo "AsyncTask Finish:Connect.PID=".posix_getpid().PHP_EOL;//

    }


    /**
     * 关闭回调方法
     * @param  SwooleServer $serv    swoole对象
     * @param  int $fd                连接句柄编号
     */
    public function onClose($serv, $fd, $reactorId)
    {
        $theWorkerRedis=$serv->redis[$serv->worker_id];

        echo " \n *** tcp total connects count is :".count($serv->connections)." ***".PHP_EOL;

        $deviceID=$this->client->get($fd, 'deviceID');
        if(isset($deviceID) && !empty($deviceID))
        {
            $status= $this->client->get($fd,'status');
            $clsmsg = $status==0 ? '':getclosetext($status);
            $sttdata= array(
                'type' => 'TBstat',
                'fd' =>$fd,
                'deviceid' => $deviceID,
                'status' => 0,
                'offtext'=>$clsmsg,
                'statustext' => 'offline',
                'time' => date('Y-m-d H:i:s',time()),
            );
            $this->sentMsgOut($serv,$sttdata,$theWorkerRedis);

            // 统计当天上线次数
            $tbMacKey = Logger::tb($deviceID);
            $closedCounts = $theWorkerRedis->hMGet($tbMacKey, [
                'cfg_connect_closed',
                'cfg_closed_by_server',
                'cfg_closed_by_client'
            ]);

            $connectClosedCounts = $closedCounts['cfg_connect_closed'] ?: 0;
            $closedByServerCounts = $closedCounts['cfg_closed_by_server'] ?: 0;
            $closedByClientCounts = $closedCounts['cfg_closed_by_client'] ?: 0;


            ++$connectClosedCounts;

            $htmsg="\n =={$clsmsg}=== heart sent/losed= {$this->client->get($fd,'hrtsd')} /{$this->client->get($fd,'hrtrsv')} ";
            if($reactorId < 0) {
                $clsmsg = " server closed fd[{$fd}] . ".$htmsg. date('Y-m-d H:i:s') . " ===\n";

                ++$closedByServerCounts;

                // 被服务器端强制中断
                Log::cfgLog($deviceID)->warning(Log::CONNECT_CLOSED, [
                    'status' => $status,
                    '断开原因' => '服务器断开: ' . Logger::getCloseReason($status),
                    '断开次数' => "总共断开{$connectClosedCounts}次, 其中服务器主动断开{$closedByServerCounts}次",
                    'fd' => $fd,
//                    '当前连接总接收流量' => $this->flow->get($fd, 'receive') / 1024 . 'KB',
//                    '当前连接总发送流量' => $this->flow->get($fd, 'send') / 1024 . 'KB',
//                    '解析错误流量' => $this->flow->get($fd, 'failed') . 'B',
                ]);
            } else {
                $clsmsg = "  client closed fd[{$fd}]. ".$htmsg. date('Y-m-d H:i:s') . " ===\n";

                ++$closedByClientCounts;
                // 客户端关闭连接
                Log::cfgLog($deviceID)->warning(Log::CONNECT_CLOSED, [
                    'status' => $status,
                    '断开原因' => '客户端断开: ' . Logger::getCloseReason($status),
                    '断开次数' => "总共断开{$connectClosedCounts}次, 其中客户端主动断开{$closedByClientCounts}次",
                    'fd' => $fd,
//                    '当前连接总接收流量' => $this->flow->get($fd, 'receive') / 1024 . 'KB',
//                    '当前连接总发送流量' => $this->flow->get($fd, 'send') / 1024 . 'KB',
//                    '解析错误流量' => $this->flow->get($fd, 'failed') . 'B',
                ]);
            }

            $theWorkerRedis->hMset($tbMacKey, [
                'cfg_connect_closed' => $connectClosedCounts,
                'cfg_closed_by_server' => $closedByServerCounts,
                'cfg_closed_by_client' => $closedByClientCounts
            ]);

//        stdLog::print($clsmsg);
            echo $clsmsg;
            echoMsg($clsmsg,$theWorkerRedis,true,'confredis','config');

//        $serv->db[$serv->worker_id]->update('device', array('cfglastouttime' => time(), 'cfgfd' => 0), array('id' => $this->client->get($fd, 'id')));

            $this->id2fd->del($this->client->get($fd, 'deviceID'));
        }

        $theWorkerRedis->del('heart:'.$fd);
        $this->client->del($fd);
        $this->heartlist->del($fd);

    }
    // 4. 正常结束Server的时候，首先结束Worker进程 kill -10
    public function onWorkerStop($server,$worker_id)
    {
        echo "\n WorkerStop[$worker_id]|pid=".posix_getpid().".\n";
        $server->workerdb=null;
        $server->redis[$worker_id] = TbRedis::close("tbswrwkr".$worker_id);
    }

    /**
     * 命令管道推送命令进程
     * @param $serv
     * @return mixed
     * @Created by: xiebaoxin@163.com
     * @DateTime: 2018/5/8 9:54
     */
    public function getProcess($serv)
    {
        for ($i = 0; $i < $this->cpu_num ; $i++) {
            $this->taskProcess[$i] = new \Swoole\Process(function ($process) use ($serv,$i){
                cli_set_process_title('Send CMD Process'.$i);
                    //连接数据库
                    while (true) {
                        try {
                            $db =  Model::init("cmdprocessdb".$i);
                            break;
                        } catch (\PDOException $e) {
                            stdLog::print('mysql connect fail', $e->getCode(), $e->getLine());
                            sleep(1);
                        }
                    }

                    while (1) {
                        try {
                            if (false ==  $redis = @TbRedis::init('configsentProcess'.$i)) {
                                echo "Redis connect faild! \n";
                                throw new \Exception("Redis connect faild");
                            }

                            break;
                        } catch (\Exception $e) {
                            echo "reconnect Redis …… \n";
                            sleep(2);
                        }
                    }


                    while (true) {
                        $data = $process->pop();
                        $data = json_decode($data, true);

                        $senttime=$this->microtime_float();
                        $data['retry']++;
                        $this->cmd->set($data['fd'], array('retry' => $data['retry'], 'timeout' => $senttime));
                        $this->client->set($data['fd'], array('lock' => 1));

                        $serv->send($data['fd'], hex2bin($data['encode']));


                        //推送给前端数据
                        $arr = array(
                            'type'     => 'send',
                            'msg'      => '发送数据'.$data['encode'],
                            'deviceID' => $this->client->get($data['fd'], 'deviceID'),
                            'command'  => $data['command'],
                            'wsfd'     => $data['wsfd'],
                            'token'    => $this->client->get($data['fd'], 'token'),
                            'before'   => $data['encode'],
                            'time'     => $senttime,
                        );
                        $redis->lPush('result', json_encode($arr));

                       try {
                            //插入日志
                            $insData = array(
                                'DeviceId'   => $this->client->get($data['fd'], 'deviceID'),  //设备ID
                                'OpSource'   => 1,                                               //操作源
                                'OpUser'     => 0,                                                 //操作用户
                                'CmdBefore'  => $data['command'],                                //加密前
                                'CmdAfter'   => $data['encode'],                                 //加密后
                                'HashVer'    => 1,                                                //md5版本
                                'RetryCount' => $data['retry'],                                //重试次数
                                'Token'      => $this->client->get($data['fd'], 'token'),        //加密token
                                'Status'     => 0,                                                 //状态(探霸有返回数据则为1,这里因为是第一次发送所以为0)
                                'Createat'   => time(),                                          //发送时间
                            );

                            if ($data['logid']) {
                                $db->update('device_command_log', $insData, array('id' => $data['logid']));
                            } else {
                                $db->insert('device_command_log', $insData);
                                $id = $db->id();
                                $this->cmd->set($data['fd'], array('logid' => $id));
                            }
                        } catch (\PDOException $e) {
                            stdLog::print('insert or update log statu fail', $e->getCode(), $e->getLine());
                            break;
                        }

                    }

            });
//            $this->processTable->set($this->taskProcess[$i],array("stt"=>1));
            $this->taskProcess[$i]->useQueue();
        }

        return $this->taskProcess;
    }


    public function microtime_float()
    {
//        return  microtime_float();
        list($usec, $sec) = explode(" ", microtime());
        return (float)sprintf('%.0f',(floatval($usec)+floatval($sec))*1000);
//        return ((float)$usec + (float)$sec);
    }

    // 6. 最后回收Master进程
    public function onShutdown( $serv )
    {
        ;//    swoole_process::wait(false);
        \Swoole\Process::kill($serv->manager_pid,0);

        for ($i = 0; $i < $this->cpu_num ; $i++) {
            //  foreach($this->processTable as $k){
            \Swoole\Process::kill($this->taskProcess[$i]);

            $this->processTable->del($this->taskProcess[$i]);
        }
    }

    /**
     * 析构方法
     */
    public function __destruct()
    {
        stdLog::print("set all device offline successful");
    }
}
